package com.demo.test.util;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.demo.test.entity.MethodParameterPo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.SneakyThrows;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 功能概述:jar规则class工具类
 *
 * @author maoxiaomeng
 * @date 2022/9/8 13:52
 */
@SuppressWarnings("all")
public class JarClassUtil {

    static Map<Class<?>, ParameterizedTypeImpl> parameterizedTypeMap = new HashMap<>();

    @SneakyThrows
    public static MethodParameterPo getFields(Class<?> cls) {
        return getFields(cls, null, null, "$", "");
    }


    /**
     * 根据实体类对象获取所有的参与和参数名称解释,类型
     *
     * @param cls cls对象
     * @return
     */
    @SneakyThrows
    public static MethodParameterPo getFields(Class<?> cls, Field fieldName, String objectName, String absoluteKey, String absoluteTypeKey) {
        if (Objects.isNull(cls)) {
            return null;
        }
        // 特殊处理基类
        if (isBasicTypeClass(cls)) {
            MethodParameterPo.DataType ruleResultType = convertToDataType(cls.getSimpleName());
            return new MethodParameterPo()
                    .setType(cls)
                    .setAbsoluteKey(absoluteKey)
                    .setKey(cls.getSimpleName())
                    .setTypeName(ruleResultType)
                    .setInfo(ruleResultType.getDesc())
                    .setTypeNameDesc(ruleResultType.getDesc());
        }
        // 去重处理,防止无限递归(树形结构)
        // 优化去重,旧的去重,是对一个类中约束,不允许重复出现类型相同的class,而实际上,相同的类型,它们只要不在同一条属性链上,就应当可以允许出现
        if (StrUtil.count(absoluteTypeKey, cls.getSimpleName()) > 1) {
            return new MethodParameterPo().setKey(cls.getSimpleName()).setType(cls).setTypeName(convertToDataType(cls.getSimpleName()));
        }

        // 记录cls父类的泛型
        Class<?> t = null;
        Class<?> originalClass = cls;
        String originalAbsoluteKey = absoluteKey;
        String originalAbsoluteTypeKey = absoluteTypeKey;
        // 获取api注解信息
        ApiModel apiModel = cls.getAnnotation(ApiModel.class);
        String info = cls.getSimpleName();
        if (StrUtil.isNotBlank(objectName)) {
            info = objectName;
        } else if (Optional.ofNullable(apiModel).isPresent()) {
            info = apiModel.value();
        }
        // 记录cls的所有成员变量
        List<Field> fieldList = new ArrayList<>();
        // 遍历所有父类,获取成员变量
        while (cls != null) {
            //获取父类以及父类所带的泛型
            Type superClassType = cls.getGenericSuperclass();
            //如果superClassType 携带泛型
            if (superClassType instanceof ParameterizedType) {
                //就把type强转为参数化泛型,并获取实际的泛型参数数组
                Type[] actualTypes = ((ParameterizedType) superClassType).getActualTypeArguments();
                if (ObjectUtil.isNotNull(actualTypes)) {
                    Type actualType = actualTypes[0];
                    if (actualType instanceof Class) {
                        t = originalClass.equals(actualType) ? null : (Class<?>) actualType;
                    }
                }
            }
            fieldList.addAll(Arrays.asList(cls.getDeclaredFields()));
            cls = cls.getSuperclass();
        }
        // 封装子节点数据
        List<MethodParameterPo> paramList = new ArrayList<MethodParameterPo>();
        for (Field field : fieldList) {
            Class<?> type = field.getType();
            // 获取api注解信息
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            if (apiModelProperty == null || apiModelProperty.hidden()) {
                continue;
            }
            String propertyInfo = apiModelProperty.value();
            if (ObjectUtil.isEmpty(propertyInfo)) {
                propertyInfo = apiModelProperty.name();
            }
            String fieldAbsoluteKey = originalAbsoluteKey + "." + field.getName();
            String fieldAbsoluteTypeKey = originalAbsoluteTypeKey + "." + field.getType().getSimpleName();
            MethodParameterPo param = new MethodParameterPo()
                    .setInfo(propertyInfo)
                    .setExample(apiModelProperty.example())
                    .setExtend(apiModelProperty.reference())
                    .setType(type)
                    .setTypeName(convertToDataType(type.getSimpleName()))
                    .setKey(field.getName())
                    .setAbsoluteKey(fieldAbsoluteKey);
            // 处理List
            Class<?> tmp;

            //如果是对象泛型 T obj;
            if (type.equals(Object.class)) {
                //todo from here  去声明的泛型map中找到真实的type
                ParameterizedTypeImpl parameterizedType = parameterizedTypeMap.get(type);
                if (parameterizedType == null) {
                    continue;
                }
                Type typeName = parameterizedType.getActualTypeArguments()[0];
                tmp = (Class<?>) typeName;
                //构建多出的层次结构
                MethodParameterPo allFields = getFields(tmp, field, propertyInfo, fieldAbsoluteKey, fieldAbsoluteTypeKey);
                param.setTypeName(convertToDataType(tmp.getSimpleName()));
                param.setType(tmp);
                paramList.add(allFields);
                continue;
            }

            if (List.class.equals(type)) {
                paramList.add(param);
                //获取该field的泛型的type  List<>
                Type tType = ((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0];
                //获取type的名称
                String typeName = tType.getTypeName();
                //处理 List<T> items typeName =  T
                if ("T".equals(typeName)) {
                    //t=xxItems.class
                    if (t != null) {
                        fieldAbsoluteTypeKey = originalAbsoluteTypeKey + "." + typeName;
                        MethodParameterPo allFields = getFields(t, field, propertyInfo, fieldAbsoluteKey, fieldAbsoluteTypeKey);
                        ArrayList<MethodParameterPo> nodeList = new ArrayList<>();
                        nodeList.add(allFields);
                        param.setChildren(nodeList);
                    }
                    continue;
                }
                //记录最里层的class  List<List<T>>
                //如果该type List<Object.class>
                if (tType instanceof Class) {
                    //记录泛型的class
                    tmp = (Class) tType;
                    fieldAbsoluteTypeKey = originalAbsoluteTypeKey + "." + tmp.getSimpleName();
                } else {
                    //type 有可能还是泛型 ,再取一层  List<List<Object.class>>
                    typeName = ((ParameterizedTypeImpl) tType).getActualTypeArguments()[0].getTypeName();
                    tmp = Class.forName(typeName);
                    fieldAbsoluteTypeKey = originalAbsoluteTypeKey + "." + typeName;
                    //构建多出的层次结构
                    MethodParameterPo innerMethodParameterPo = new MethodParameterPo();
                    innerMethodParameterPo
                            .setType(List.class).setExample("-")
                            .setTypeName(convertToDataType(List.class.getSimpleName())).setKey("_child")
                            .setAbsoluteKey(fieldAbsoluteKey + "." + field.getName());
                    List<MethodParameterPo> childList = new ArrayList<>();
                    childList.add(innerMethodParameterPo);
                    param.setChildren(childList);
                    param = innerMethodParameterPo;
                }
                Class childClass = tmp;

                List<MethodParameterPo> childList = new ArrayList<>();
                if (ClassUtil.isBasicType(childClass) || "java.lang.String".equals(childClass.getName())) {
                    MethodParameterPo MethodParameterPo = new MethodParameterPo()
                            .setType(childClass)
                            .setAbsoluteKey(fieldAbsoluteKey)
                            .setKey("-")
                            .setInfo(propertyInfo);
                    childList.add(MethodParameterPo);
                    param.setChildren(childList);
                    continue;
                }
                childList.add(getFields(childClass, field, propertyInfo, fieldAbsoluteKey, fieldAbsoluteTypeKey));
                param.setChildren(childList);
                continue;
            }


            // 处理java类
            if (type.getName().startsWith("java") || type.isPrimitive()) {
                paramList.add(param);
                continue;
            }
            // 递归处理
            // 为了解决类复用的问题,同一个类复用到不同的地方,类名称不一样,正常来说应该一致旧的写法是在递归到类内部获取类上的注解含义
            // 新的写法是在获取字段的时候,如果该field是对象,则获取对象含义,递归时传入,参看 百融-借贷意向验证的出参
            MethodParameterPo fields = getFields(type, field, propertyInfo, fieldAbsoluteKey, fieldAbsoluteTypeKey);
            paramList.add(fields);
        }
        if (paramList.isEmpty()) {
            paramList = null;
        }
        String fieldNameStr = fieldName == null ? originalClass.getSimpleName() : fieldName.getName();
        return new MethodParameterPo()
                .setInfo(info)
                .setType(originalClass)
                .setKey(fieldNameStr)
                .setChildren(paramList)
                .setAbsoluteKey(originalAbsoluteKey);
    }

    private static List<Field> getAllFields(Class cls, Class t, Class originalClass, List<Field> fieldList) {
        while (cls != null) {
            //获取父类以及父类所带的泛型
            Type superClassType = cls.getGenericSuperclass();
            //如果superClassType 携带泛型
            if (superClassType instanceof ParameterizedType) {
                //就把type强转为参数化泛型,并获取实际的泛型参数数组
                Type[] actualTypes = ((ParameterizedType) superClassType).getActualTypeArguments();
                if (ObjectUtil.isNotNull(actualTypes)) {
                    Type actualType = actualTypes[0];
                    if (actualType instanceof Class) {
                        t = originalClass.equals(actualType) ? null : (Class) actualType;
                    }
                }
            }
            fieldList.addAll(Arrays.asList(cls.getDeclaredFields()));
            cls = cls.getSuperclass();
        }
        return fieldList;
    }

    protected static Type[] getGenericList(Class<?> cls) {
        Type obj = cls.getGenericInterfaces()[0];
        ParameterizedType parameterizedType = (ParameterizedType) obj;
        return parameterizedType.getActualTypeArguments();
    }

    protected static Class getResParameterClass(Class<?> cls) {
        Type obj = cls.getGenericInterfaces()[0];
        ParameterizedType parameterizedType = (ParameterizedType) obj;
        Class typeArgument = (Class) parameterizedType.getActualTypeArguments()[1];
        return typeArgument;
    }


    /**
     * 根据类型直接获取参数解释名称
     *
     * @param cls 实体的类class
     * @return java.util.List<com.hare.service.function.callapi.po.MethodParameterPo>
     * @author maoxiaomeng
     */
    public static List<MethodParameterPo> getParameter(Class<?> cls) {
        List<Field> fieldList = new ArrayList<>();
        while (cls != null) {
            //获取父类以及父类所带的泛型
            Type superClassType = cls.getGenericSuperclass();
            //如果superClassType 携带泛型
            fieldList.addAll(Arrays.asList(cls.getDeclaredFields()));
            cls = cls.getSuperclass();
        }
        Class currentClass = cls;
        return fieldList.stream().filter(e -> !"serialVersionUID".equals(e.getName())).map(e -> {
            if (e.isAnnotationPresent(ApiModelProperty.class)) {
                ApiModelProperty swagger = e.getAnnotation(ApiModelProperty.class);
                if (!swagger.hidden()) {
                    return new MethodParameterPo()
                            .setInfo(swagger.value())
                            .setType(e.getType())
                            .setNecessary(swagger.required())
                            .setKey(e.getName())
                            .setExample(swagger.example());
                }
                return null;
            } else {
                throw new RuntimeException("类:" + currentClass.getSimpleName() + " 属性名:" + e.getName() + " 未添加ApiModelProperty");
            }
        }).filter(ObjectUtil::isNotEmpty).collect(Collectors.toList());
    }

    public static MethodParameterPo.DataType convertToDataType(String typeName) {
        if (typeName == null) {
            return MethodParameterPo.DataType.OBJ;
        }
        if (typeName.contains(".")) {
            //java.lang.String = >String
            String[] split = typeName.split("\\.");
            typeName = split[split.length - 1];
        }


        switch (typeName) {
            case "List":
            case "Set":
            case "Collection":
                return MethodParameterPo.DataType.ARRAY;
            case "Boolean":
                return MethodParameterPo.DataType.BOOLEAN;
            case "Double":
            case "Number":
                return MethodParameterPo.DataType.DOUBLE;
            case "String":
                return MethodParameterPo.DataType.STRING;
            case "Integer":
                return MethodParameterPo.DataType.INT;
            default:
                return MethodParameterPo.DataType.OBJ;
        }
    }

    public static boolean isBasicTypeClass(Class<?> clazz) {
        return ClassUtil.isBasicType(clazz) || clazz.equals(String.class);
    }
}
